home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / MotifApp / Extras / ch0.txt < prev    next >
Encoding:
Text File  |  1995-05-03  |  60.9 KB  |  1,359 lines

  1. A Tutorial
  2. Object-Oriented Programming  
  3. with C++
  4.  
  5.  
  6.  
  7. This material was originally written for the book Object-Oriented
  8. Programming with C++ and OSF/Motif, by Douglas Young, Prentice Hall, 1992.
  9. It was cut from that text because it seemed slightly off the central topic
  10. of that book, which is how to use object-oriented techniques to write X and
  11. Motif applications. Rough edges, due to its extraction in a somewhat
  12. incomplete state from the book for which it was originally written,
  13. undoubtedly exist. It may be useful to some as a tutorial introduction to
  14. object-oriented programming.
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22. This tutorial introduces the fundamental concepts of object-oriented
  23. programming and shows how C++ supports this technique. Section 1 contrasts
  24. object-oriented programming with more traditional techniques and provides
  25. an overview of the characteristics and advantages of the object-oriented
  26. approach. Section 2 discusses individual aspects of object-oriented
  27. programming, with an emphasis on the related features of C++. Section 3
  28. presents some of the features of C++ that, while not directly related to
  29. object-oriented programming, are particularly useful in conjunction with an
  30. object-oriented style.
  31.  
  32. 1    What Is Object-Oriented Programming?
  33.  
  34. Most software developers realize that the process of writing software is
  35. too complex to allow applications to be written as a single, continuous
  36. stream of instructions. Beginning programmers soon learn to break large
  37. programs into smaller components, often called subroutines, functions,
  38. procedures, or modules. Each of these components corresponds to some
  39. subset of the entire task performed by the program. Therefore, one of the
  40. first questions that must be answered before beginning any significant
  41. programming effort is, "how can the problem be split into smaller
  42. problems?" Once this question is answered, the resulting smaller problems
  43. become the modules or subcomponents of the larger program.
  44.  
  45. This technique of solving large or complex problems by breaking a problem
  46. down into smaller pieces that can be solved individually is called
  47. decomposition. The main difference between object-oriented programming and
  48. more traditional approaches lies in the way in which problems are
  49. decomposed. The traditional, non-object-oriented approach to decomposing
  50. problems focuses on the operations or activities that need to be performed.
  51. The programmer begins by breaking a problem into several tasks. These tasks
  52. are then divided into sub-tasks, which can be broken down even further. At
  53. some point, each task becomes small enough that it is practical to
  54. implement it as a relatively small function.
  55.  
  56. The function-oriented approach is undoubtedly familiar to all programmers,
  57. but let's look at a simple example so we can contrast it with an
  58. object-oriented approach. The problem to be solved is the design and
  59. implementation of a first-in-last-out (FILO) queue, also known as a stack.
  60. This problem can be split into two parts: placing data onto the stack, and
  61. removing data from the stack.
  62.  
  63. As typically implemented, the stack is a data structure of some type
  64. (perhaps an array), declared and allocated somewhere in the program. A pair
  65. of functions, push() and pop(), perform the two basic tasks mentioned
  66. above. The stack data structure is passed as an argument to the push() and
  67. pop() functions, which modify the stack by adding or removing items. Figure
  68. 1 shows a data flow diagram of a program using a functional implementation
  69. of a stack. In this figure, the circles represent procedures and the arrows
  70. represent the flow of the data named beside the arrows.
  71.  
  72. In this scenario, it is the responsibility of the programmer who uses these
  73. functions to ensure that all parts of the program use the correct data
  74. structure to represent the stack. If the programmer wishes to use a
  75. different representation for the stack (perhaps a linked list instead of an
  76. array), he or she must also change all parts of the program that refer to
  77. the stack. When using traditional programming techniques, the data and the
  78. functions that operate on that data tend to be distributed throughout a
  79. program. A change to one part of a system is likely to require changes to
  80. many other parts as well. In the example in Figure 1, the main body of the
  81. program and both the pop() and push() functions would have to be modified
  82. to support any new representation.
  83.  
  84.  
  85.  
  86. Figure 1  Using a procedural stack implementation.
  87.  
  88. Instead of focusing on the tasks to be performed, object-oriented
  89. programmers design software around the objects in a system. In an
  90. object-oriented program, a stack would undoubtedly be implemented as an
  91. object. The stack object would be a self-contained entity that supports two
  92. operations named push and pop. The actual data structure used to represent
  93. the stack would be hidden inside the object as would the implementation of
  94. the push and pop operations. Applications would interact with the stack
  95. only by sending push and pop messages to the stack object, not by passing
  96. the stack to separate push() and pop() functions. Instead of viewing a
  97. stack as three distinct entities (two functions and a data structure),
  98. object-oriented programmers view a stack as a single entity that can handle
  99. several messages.
  100.  
  101. Figure 2 represents an object-oriented implementation of a stack. Here, the
  102. circle represents a procedure (main), while the rectangle represents a
  103. stack object. The object has two input ports, labeled push and pop. The
  104. arrows connected to these input areas represent a message sent to the
  105. object, and also show the flow of data. Here, the data includes the items
  106. pushed or popped from the stack.
  107.  
  108.  
  109.  
  110. Figure 2 Using a stack object.
  111.  
  112. To the outside world, the stack object is a "black box." The only thing we
  113. know about it is that we can push data onto the stack, and pop the data
  114. back off. This stack object could be replaced with any other stack object
  115. that has the same external interface. Applications that use one stack
  116. implementation should continue to work, even if the original stack is
  117. replaced by another one that has the same external interface but a
  118. completely different internal implementation.
  119.  
  120. The difference between these two approaches has some important
  121. consequences. A Stack object completely hides the data used to represent
  122. the stack from the other parts of the program and also hides the
  123. implementation of the operations on the stack. The stack offers a
  124. well-defined external interface, provided by the operations push and pop.
  125. As long as that interface remains constant, the programmer is free to
  126. change the internal data structure used to represent the stack, or the
  127. implementation of the operations on that data. When using object-oriented
  128. techniques, all data and the functions that operate on that data are
  129. contained in one location, instead of being distributed throughout an
  130. application. Therefore, the implementation of individual objects can be
  131. changed without affecting other parts of a system.
  132.  
  133. Let's assume that a programmer decides to use an existing stack object from
  134. a library, which is probably written and maintained by someone else. Now,
  135. suppose that the programmer who supports that library implements a faster,
  136. more robust version of a stack by changing the data structure used
  137. internally by the stack. Changing the data structure will most likely also
  138. require the push and pop operations to be reimplemented as well. However,
  139. the external interface to push and pop should not have to change. Because
  140. such changes only affect the internal implementation and not the external
  141. interface to the stack, programmers who previously used the older version
  142. can simply relink with the new library and receive the benefits of the new
  143. stack, without changing any code.
  144.  
  145. This is far less likely to be the case with a procedural stack library
  146. package, such as that described in the previous section, because the
  147. non-object-oriented approach is more likely to expose internal details,
  148. such as the precise definition of the stack data structure. Objects
  149. encapsulate data and operations on the object's data to insulate
  150. applications from the internal implementation details of that object. This
  151. does not mean, of course, that a careful programmer can't design and
  152. implement highly encapsulated data structures in non-object-oriented
  153. languages. It is also possible to write poorly encapsulated data structures
  154. in most object-oriented languages. The difference is in the degree of
  155. support various languages provide for encapsulation and data abstraction.
  156.  
  157. So far, we have not stated specifically what object-oriented programming
  158. is. Unfortunately, it is difficult to develop a precise definition of
  159. object-oriented programming that everyone can agree on, although many
  160. people have tried. In fact, there are many, often conflicting, definitions.
  161. The previous sections hinted at one definition, which can be stated as
  162. follows:
  163.  
  164. Object-oriented programming is a technique that emphasizes the objects in a
  165. system rather than the tasks the system performs.
  166.  
  167. This simple idea is the heart of the object-oriented approach, but it
  168. leaves many details to the imagination. The previous discussion
  169. concentrates on one element of object-oriented programming, often called
  170. data abstraction. Data abstraction is an important characteristic of
  171. object-oriented programming, but it is not the only one. The following are
  172. some features typically associated with object-oriented programming:
  173.  
  174. o    An object is a cohesive package of data and functions that operate
  175. on that data.
  176.  
  177. o    An object is an instance of a class, which defines the structure
  178. and behavior of all objects that belong to the class.
  179.  
  180. o    Some or all of an object's data can be specified as private to the
  181. object, such that this data cannot be accessed by any part of a program
  182. outside the object. This is referred to as encapsulation or data
  183. abstraction.
  184.  
  185. o    The operations supported by an object are often referred to as
  186. methods. Methods are invoked by sending a message to an object.
  187.  
  188. o    Polymorphism allows different types of objects to respond to the
  189. same message in different ways, without requiring the program to know the
  190. object's exact type.
  191.  
  192. o    Inheritance allows classes to share the behavior of other classes.
  193.  
  194. One reason object-oriented programming is difficult to define is that
  195. "object-oriented" is really just a way of thinking. It is an attitude; an
  196. approach to designing a system. The specific details listed above are just
  197. a few features of various languages that programmers can use to implement
  198. an object-oriented design.
  199.  
  200. Notice that programmers can use almost any language to develop software
  201. using object-oriented techniques. The Xt Intrinsics library, on which Motif
  202. is based, provides an example of a system that implements most of the
  203. characteristics listed above in ordinary C. However, an object-oriented
  204. language can make the task much easier for the programmer. While it is
  205. possible, developing an object-oriented system in C requires a great deal
  206. more effort than when using a language like C++, which directly supports
  207. object-oriented programming. The following sections examine each of the
  208. characteristics listed above in more detail, and demonstrate how C++
  209. supports these object-oriented techniques.
  210.  
  211. The Benefits of Object-oriented Programming
  212.  
  213. The previous section discusses some of the benefits of data abstractions.
  214. However, object-oriented programming offers other advantages, as well.
  215. Object-oriented techniques can benefit software developers in several ways,
  216. which include:
  217.  
  218. 1.    Reusability. Objects provide a way to develop software that can be
  219. used in multiple parts of a program, or even by multiple projects.
  220. Well-designed classes are often largely self-contained and have few
  221. external dependencies. This allows programmers to treat objects as
  222. components that can be plugged in wherever they are needed. Brad Cox
  223. [Cox86] stresses the idea that objects are the software equivalent of an
  224. integrated circuit that can be plugged into many different circuit boards.
  225. Cox refers to this type of object as a Software-IC to emphasize the
  226. analogy. The promise of reusability is one of the greatest attractions of
  227. object-oriented programming. If a software component can be written once
  228. but used many times, programmers will be more productive and programs will
  229. be more robust.
  230.  
  231. 2.    Maintainability. Even when a class is used only once, the emphasis
  232. object-oriented programming places on clean interfaces between
  233. self-contained software modules is an important benefit in itself. Because
  234. objects are self-contained, changes made to any particular object are less
  235. likely to affect other objects in the system. Maintenance typically
  236. consumes a far greater part of the software lifecycle than initial
  237. development, so any technique that can make software easier to maintain is
  238. worth exploring.
  239.  
  240. 3.    Rapid-prototyping and development. Inheritance allows programmers
  241. to create new types of objects by extending and altering existing types.
  242. This often allows programmers to prototype complex systems with surprising
  243. speed.
  244.  
  245. 4.    Extensibility. Most object-oriented languages allow programmers to
  246. declare types of objects as new data types. These types allow programmers
  247. to effectively extend the language. Also, most object-oriented languages
  248. provide mechanisms that allow programmers to extend the capabilities of
  249. existing types of objects.
  250.  
  251. 5.    Conceptual consistency. In many cases, objects within a program
  252. correspond directly to objects in the real world. In traditional
  253. programming, programmers have to construct a complex mapping between the
  254. abstract model of what the program does and the functions and procedures
  255. that implement that model. In object-oriented systems, the objects often
  256. correspond directly to the abstractions modeled by the program, which can
  257. make programs easier to understand.
  258.  
  259. The extent to which these claimed benefits of object-oriented programming
  260. have a measurable impact on real software development is still open to
  261. debate. Solid evidence is hard to find or substantiate. Those who believe
  262. in object-oriented techniques often claim phenomenal improvements in
  263. productivity, quality of code, and maintainability later in the software
  264. lifecycle. Others point to projects that used object-oriented techniques
  265. and failed. In either case, it is seldom clear whether object-oriented
  266. programming was the key to the claimed success or failure, or whether other
  267. more significant factors affected the final outcome.
  268.  
  269. In spite of the lack of solid evidence, object-oriented programming is
  270. widely recognized as a valuable tool, applicable to a wide variety of
  271. situations. Programmers who learn to apply object-oriented techniques
  272. seldom return willingly to their earlier programming styles.
  273.  
  274. 2    The Elements of Object-oriented Programming
  275.  
  276. Section 1 mentions several common characteristics often identified with
  277. object-oriented programming. The following sections examine each of these
  278. features in more detail. In addition to discussing basic object-oriented
  279. concepts, we will see how C++ supports classes, inheritance, and other
  280. object-oriented techniques.
  281.  
  282. Objects
  283.  
  284. An object is a self-contained package that encapsulates data and procedures
  285. that operate on the object's data. From an abstract perspective, an object
  286. is a "thing" - a programmatic abstraction that often closely models some
  287. entity in the real world. From a less abstract viewpoint, an object is
  288. simply an instance of a complex data structure that includes both data and
  289. functions. Object-oriented languages such as C++ provide convenient ways to
  290. declare, implement and use these data structures in an object-oriented
  291. style.
  292.  
  293. The stack described in the previous section is one example of an object,
  294. but the list of things that can be modeled as objects is endless. For
  295. example, consider an electronic mail program. The program receives mail
  296. from a low-level mail service such as the UNIX sendmail program, and allows
  297. the user to view and manipulate individual messages or groups of messages.
  298.  
  299. One way to design such a program is to model each incoming mail message as
  300. an object. The data in the object might include such things as the text of
  301. the message, who the mail is for, who it is from, the date on which it was
  302. sent, and so on. This object, which we can call a mailMessage object, could
  303. support all the operations the user might wish to perform on any individual
  304. mail message. For example, the mailMessage object might support:
  305.  
  306. o    A send operation that causes the message to be sent to its
  307. destination
  308.  
  309. o    A reply operation that sends a message back to whoever originated
  310. the message
  311.  
  312. o    A forward operation that sends a copy of the message to someone
  313. else
  314.  
  315. o    A print operation that sends the text of the message to a printer
  316.  
  317. o    A view operation that displays the contents of the message on the
  318. screen
  319.  
  320. o    A delete operation that deletes the contents of the message, and
  321. the object itself.
  322.  
  323. There may be many other objects in the mail system as well. For example,
  324. the program might have an inbox object, an outbox object, and perhaps other
  325. container objects that can store collections of related mailMessage
  326. objects. Such objects would each maintain collections of objects, and
  327. support operations such as:
  328.  
  329. o    An add operation that adds a mailMessage object to the container
  330.  
  331. o    A remove operation that removes a mailMessage object from the
  332. container
  333.  
  334. o    A sort operation that sorts the container's contents in various
  335. ways
  336.  
  337. o    A view operation that displays a list of the container's contents
  338.  
  339. o    A print operation that sends a list of the message objects in the
  340. container to a printer.
  341.  
  342. Another object that could be useful in such a system is a dispatcher object
  343. that watches for new mail and routes new messages to the inbox. We could
  344. call this object a postMaster object. The postMaster object could also
  345. route outgoing messages from the outbox to some external electronic mail
  346. service.
  347.  
  348. Figure 3 shows how these objects work together in a simple mail tool. The
  349. postMaster object receives messages from the external mail system, and
  350. routes the incoming mailMessage objects to the user's inbox object. This
  351. object stores all unread mailMessage objects. The user can send view
  352. messages, print messages, and so on by sending messages to the inbox and
  353. the mailMessage objects through some suitable user interface.
  354.  
  355. The user can also create new mailMessage objects, through some interface
  356. not shown here, and place them in an outbox object. From there, they are
  357. sent to the postMaster object for processing and routing to the appropriate
  358. destination.
  359.  
  360.  
  361.  
  362. Figure 3 An object-oriented architecture for a mail program.
  363.  
  364. This example illustrates how a simple system can be viewed as a collection
  365. of independent objects. Each object in the mail system is responsible for
  366. performing certain tasks and maintaining its own state. Each object
  367. interacts with others, but performs its primary task independently.
  368.  
  369. Classes
  370.  
  371. In object-oriented programming, the word class refers to a category of
  372. structurally-identical objects.  For example, in the mail program discussed
  373. above, all mailMessage objects would belong to a class known as the
  374. MailMessage class. The MailMessage class would describe the characteristics
  375. of all mailMessage objects and the operations that could be performed on
  376. those objects. When a new mail message arrives, the postMaster object
  377. creates a unique object to represent that particular message. Each
  378. individual object is an instance of the MailMessage class. Objects are
  379. created by instantiating a class.
  380.  
  381. A class serves as a template for creating objects. The template defines the
  382. type of data stored in an object and the operations supported by an object
  383. instantiated from that class. However, the actual data stored in each
  384. individual mailMessage object may vary; each instance is unique. One
  385. instance of the MailMessage class may contain a status report to be sent to
  386. a co-worker, while another might contain a request for information to be
  387. broadcast to a large mailing list.
  388.  
  389. The various container objects (inbox, outbox) in the mail system might be
  390. different instances of a Container class. Each instance of the Container
  391. class in the mail application would then support the same operations, but
  392. be used in a different way, and contain different mailMessage objects.
  393.  
  394. Creating Classes in C++
  395.  
  396. C++ directly supports classes with a new data type, known appropriately as
  397. a class. The word class is a keyword in C++. A C++ class is much like a
  398. struct in C, but has some additional properties.  Let's return to our
  399. earlier example and see how a Stack class might be implemented in C++. A
  400. very simple C++ class that represents an integer stack can be declared as
  401. follows:
  402.  
  403. class Stack {
  404.  
  405.   protected:
  406.  
  407.     int  *data;        // Items in the stack
  408.     int   top;         // Index of next open slot
  409.  
  410.   public:
  411.  
  412.     Stack();           // Constructor
  413.     ~Stack();          // Destructor
  414.     int  pop();        // Remove an item from stack
  415.     void push ( int ); // Add an item to stack
  416. };
  417.  
  418. This declaration describes a new user-defined data type called Stack that
  419. includes the data used to represent the stack and four functions that
  420. operate on that data. In addition to the word class, this example also
  421. contains two other keywords, protected and public, which will be explained
  422. as we go on. In many ways, a C++ class behaves like a C structure, but
  423. there are several significant differences. Let's look at these, one at a
  424. time.
  425.  
  426. Data Members
  427.  
  428. Like C structures, C++ classes contain members, which can be data or
  429. pointers to functions. The Stack class described above contains two data
  430. members. The first is a pointer to an integer, named data, that represents
  431. the items on the stack. The second data member, top serves as an index that
  432. marks the top of the stack. These data members cannot be accessed outside
  433. the class because they are declared in the protected portion of the class.
  434.  
  435. Instantiating C++ Classes
  436.  
  437. In C++, a class is treated the same as an instance of any other
  438. programmer-defined data type. An instance of a class can be created in the
  439. same way as any other data structure. Instantiating a class allocates
  440. memory for an object. Objects can be static, automatic, or dynamic like
  441. other variables.  The following example creates two instances of the Stack
  442. class as automatic variables:
  443.  
  444. simplefunction()
  445. {
  446.     Stack aStack, anotherStack; // Declare two Stack objects
  447.  
  448.     // Push one value on each stack
  449.  
  450.     aStack.push ( 10 );
  451.     anotherStack.push ( 20 );
  452. }
  453.  
  454. This function creates two unique and independent instances of the Stack
  455. class. The variables aStack and anotherStack are automatic variables that
  456. exist within the scope of simplefunction().
  457.  
  458. When using an object-oriented programming style, objects are often
  459. allocated dynamically on the heap. In this case, the object is usually
  460. declared as a pointer and then allocated using the C++ new operator. The
  461. new operator is similar to malloc(), normally used in C. However, new is
  462. the preferred way to allocate memory in C++, and must be used to
  463. instantiate classes. Changing the above example to use the new operator to
  464. allocate the Stack object results in the following code segment:
  465.  
  466. simplefunction()
  467. {
  468.     Stack *aStack       = new Stack(); // Allocate a Stack object
  469.     Stack *anotherStack = new Stack(); // Allocate a Stack object
  470.  
  471.     // Push one value on each stack
  472.  
  473.     aStack->push ( 10 );
  474.     anotherStack->push ( 20 );
  475.  
  476.     delete aStack;           // Free the object
  477.     delete anotherStack;     // Free the object
  478. }
  479.  
  480. In the first example, the aStack and anotherStack objects cease to exist
  481. when the program leaves the scope of simplefunction(), like any automatic
  482. variable. However, objects allocated on the heap exist until they are
  483. explicitly deleted. C++ provides a delete operator that should be used to
  484. free the memory used by objects created with new.
  485.  
  486. Encapsulation
  487.  
  488. One of the most important and useful concepts of object-oriented
  489. programming is encapsulation.  Encapsulation simply means that an object
  490. binds some data and the valid operations on that data into a cohesive
  491. package. Furthermore, encapsulation implies that the contents of an object
  492. cannot be accessed directly by other objects, except through an interface
  493. defined by the class.
  494.  
  495. C++ provides three different levels of encapsulation. Members can be
  496. completely accessible to other objects or functions, partially accessible,
  497. or completely private to the class. In the previous stack example, the two
  498. data members are preceded by the keyword protected, which indicates that
  499. they are normally not accessible outside the class. For example, if we
  500. tried to write a program that contains the statements
  501.  
  502. Stack myStack;
  503. int x = myStack.data[0]; // Error!
  504.  
  505. the C++ translator or compiler would report an error at compile time.
  506. Protected data members can only be accessed by the member functions that
  507. belong to that class, or by member functions that belong to classes derived
  508. from that class. These new terms will be discussed shortly. C++ also
  509. supports a private keyword that declares members to be completely private
  510. to the class. Only functions declared as part of the class can access
  511. private members.
  512.  
  513. C++ supports a mechanism that allows the programmer to make the internal
  514. implementation of a class accessible to other selected functions and
  515. classes, without exposing it to the rest of a program.  Any class may
  516. declare an individual function or an entire class to be a friend. Functions
  517. declared as friends have complete access to the declaring class's private
  518. and protected members. Declaring a class as a friend allows all functions
  519. that are members of the friend class to access the declaring class's
  520. private and protected members.
  521.  
  522. Methods and Messages
  523.  
  524. In common object-oriented terminology, the operations supported by an
  525. object are called methods.  The term method comes from the Smalltalk
  526. language and has been adopted by many other languages. A method is just a
  527. function that is encapsulated within a class, and that can access and
  528. operate on the data belonging to objects instantiated from that class. In
  529. most object-oriented languages, including C++, there is only one copy of
  530. the code that implements each method. The method is defined by the class,
  531. but operates on the data of individual instances of that class.
  532.  
  533. Rather than speaking of "calling" a method as one might call a function,
  534. the action of invoking a method is often referred to as "sending" a
  535. message. Messages are sent to objects, which causes the appropriate class
  536. method to be called. The idea of sending messages often provides a very
  537. natural way to think about the actions and interactions of objects in a
  538. system. For example the objects in the mail system discussed earlier in
  539. this tutorial support many messages. In the mail system example, it seems
  540. reasonable to talk about sending the postMaster object a deliverMail
  541. message or a getNewMail message. This works well because it is easy to
  542. visualize the postMaster object as a person to whom one can ask, "Do I have
  543. any mail?", or "Please deliver my letter First Class."
  544.  
  545. Some other messages suggested for the mail system don't make as much sense.
  546. A mail message seems like an inanimate object, and sending a "print
  547. yourself" message doesn't have as clear an analogy in the real world.
  548. Still, this type of anthropomorphism is common in object-oriented
  549. programming, and is quite useful. It is typical to consider all objects to
  550. be active entities, capable of independent action. Thus we can talk about
  551. telling a mail message object to "deliver yourself," or a graphical object
  552. to "draw yourself."
  553.  
  554. C++ Member Functions
  555.  
  556. In C++, methods are called member functions. Member functions are simply
  557. functions declared as part of a class. In the stack example, pop() and
  558. push(), are member functions that belong to the Stack class. The two other
  559. functions, Stack() and ~Stack() are also member functions, but are special.
  560. They will be discussed shortly. This tutorial uses the term method when
  561. discussing object-oriented programming in general, and uses the C++ term,
  562. member function when discussing C++ classes and functions. When referring
  563. to a call to a member function, this tutorial uses either traditional
  564. object-oriented terminology ("sending a message to an object") or the
  565. terminology favored by C++ ("calling a member function for an object").
  566.  
  567. Member functions must be declared as part of the class to which they
  568. belong. For example, the Stack class declaration shown on page 8 includes a
  569. pop() member function. Member function implementations must specify the
  570. class to which the function belongs by preceding the function name by the
  571. class name and two colons. For example, the pop() member function can be
  572. written like this:
  573.  
  574. int Stack::pop()  // Remove an item from the stack
  575. {
  576.     // Check for underflow before decrementing the 
  577.     // stack index and returning a value
  578.  
  579.     if ( top > 0 ) 
  580.        return ( data[--top] );
  581.     // Otherwise report an error condition
  582. }
  583.  
  584. Notice that the pop() member function can access protected data members of
  585. the class, and can also refer to them directly by name, without any
  586. qualification. C++ uses a hidden argument to all member functions, whose
  587. name is this, to implement this feature. The hidden argument provides a
  588. pointer to the instance on which the function is to operate.
  589.  
  590. The Stack class's push() member function could be written as:
  591.  
  592. void Stack::push ( int item ) // Add an integer to the stack
  593. {
  594.      data[top++] = item;  // Increment the index after adding item
  595. }
  596.  
  597. In C++, the object-oriented notion of sending a message corresponds to
  598. invoking the appropriate member function. C++ uses a familiar C-like
  599. syntax to invoke member functions, like this:
  600.  
  601. Stack aStack;             // Create a Stack object
  602. aStack.push ( 10 );       // Add an item to the stack
  603. aStack.pop();           // Remove the item
  604.  
  605. If aStack is declared as a pointer to a Stack object, member functions are
  606. invoked like this:
  607.  
  608. Stack *aStack = new Stack(); // Create a Stack object
  609. aStack->push ( 10 );         // Add an item to the stack
  610. aStack->pop();             // Remove the item
  611.  
  612. Constructors and Destructors
  613.  
  614. Every C++ class has several special member functions, including a
  615. constructor and a destructor. The constructor is called each time the class
  616. is instantiated and provides a way for the programmer to initialize the new
  617. object's data members. The constructor always has the same name as the
  618. class to which it belongs. So, in the stack example, the Stack() member
  619. function is the constructor for the Stack class. We can write the Stack
  620. constructor as:
  621.  
  622. Stack::Stack()  // Constructor
  623. {
  624.     top  = 0;            // Initialize to next available slot
  625.     data = new int[20];  // Allocate an array of 20 ints
  626. }
  627.  
  628. The Stack constructor initializes the top of the stack to indicate the
  629. first entry in the data array, and allocates memory for twenty items on the
  630. stack.
  631.  
  632. The class's destructor is called whenever an instance of the class is
  633. freed. This can occur because an object declared as an automatic variable
  634. goes out of scope, or because a dynamically allocated object is explicitly
  635. freed using the delete operator. The class destructor should free any
  636. memory allocated by the object, and perform any other cleanup that needs to
  637. be done when the object is destroyed.
  638.  
  639. A destructor does not need to explicitly delete the object itself. For
  640. example, the Stack destructor only needs to free the memory allocated in
  641. the constructor. The destructor has the same name as the class to which it
  642. belongs, but is preceded by a tilde ("~") character. We can write the Stack
  643. destructor like this:
  644.  
  645. Stack::~Stack()   // Destructor
  646. {
  647.     delete []data;  // Free array allocated by constructor
  648. }
  649.  
  650. Note that C++ requires the array brackets, as shown in this example, when
  651. deleting an array.  The C++ memory allocator does not maintain information
  652. about whether a piece of memory is an array or not. In the situation
  653. demonstrated here, no harm would be done by using
  654.  
  655. delete data;
  656.  
  657. However, because C++ would not know that data is an array, it would not
  658. call destructors for the items in the array. For an integer array like
  659. data, forgetting the array designation is inconsequential, but this error
  660. could pose a problem if data was an array of objects.
  661.  
  662. Inheritance
  663.  
  664. An essential feature of most object-oriented programming languages is the
  665. ability of a class to inherit the characteristics of another class. When a
  666. class inherits the features of another, the inheriting class is said to
  667. be a subclass of the other. The class whose features are inherited is known
  668. as a superclass of the inheriting class. Inheritance relationships can
  669. extend over many levels. That is, any given superclass may itself be a
  670. subclass of another class.
  671.  
  672. There are two basic types of inheritance: single inheritance and multiple
  673. inheritance. Figure 4 shows a typical single inheritance hierarchy, in
  674. which each class directly inherits the features of one other class, at
  675. most.
  676.  
  677.  
  678.  
  679. Figure 4 A single inheritance class hierarchy.
  680.  
  681. Here, the Vehicle class defines some basic characteristics (shown in
  682. italics) common to all vehicles. The Vehicle class has three subclasses:
  683. AirVehicle, LandVehicle, and WaterVehicle. Each of these classes inherits
  684. all the characteristics defined by the Vehicle class, but adds additional
  685. features unique to that type of vehicle. In turn, each of these classes has
  686. subclasses that inherit the characteristics of Vehicle, plus those of their
  687. immediate superclass, and add additional characteristics. So, a Jet is a
  688. Vehicle that moves, carries passengers, requires fuel, flies, and has
  689. wings.
  690.  
  691. Multiple inheritance, which is used less frequently, allows a class to
  692. inherit the characteristics of more than one immediate superclass.
  693.  
  694. Inheritance in C++
  695.  
  696. C++ supports both single and multiple inheritance, although single
  697. inheritance is the most common.  In C++, a superclass is known as a base
  698. class, while a subclass is called a derived class. As with other
  699. object-oriented terminology, this tutorial uses the more traditional
  700. object-oriented terms in the general discussion of object-oriented
  701. programming in this tutorial and leans toward the C++ terminology in the
  702. context of C++ examples.
  703.  
  704. Let's see how single inheritance works in C++. The stack class described
  705. earlier has many deficiencies. First, the stack holds only twenty items.
  706. Furthermore, the class does not check for overflow. Inheritance provides a
  707. way to extend the Stack class and change or improve it to fit our needs
  708. without changing the original class. Let's derive a new class from Stack
  709. that checks for stack overflow. This class retains the twenty item
  710. limitation.
  711.  
  712. In C++, a derived class can be declared using the following syntax:
  713.  
  714. class SafeStack : public Stack { 
  715.  
  716.   public:
  717.  
  718.     SafeStack();       // Constructor
  719.     void push ( int ); // Overrides Stack::push
  720. };
  721.  
  722. This declaration creates a new class, the SafeStack class, which is a
  723. subclass of Stack. To use C++ terminology, SafeStack is derived from Stack.
  724. The keyword public on the first line indicates that the public members of
  725. Stack are treated as public members of SafeStack as well. Further,
  726. protected members of Stack are treated as protected members of SafeStack.
  727. SafeStack does not have access to private members of Stack. The SafeStack
  728. class supports the data and top members, as well as the pop() member
  729. function provided by the class Stack, just as if SafeStack had declared
  730. these members itself.
  731.  
  732. The SafeStack class redefines the push() member function, overriding the
  733. push() function inherited from the Stack base class. The new member
  734. function is written as follows:
  735.  
  736. void SafeStack::push ( int item ) // Add an item to the stack
  737. {
  738.     if ( top < 20 )          // Check for overflow
  739.         data[top++] = item;  // Add item and increment index
  740.     else
  741.        ;   // Handle error condition here
  742. }
  743.  
  744. C++ Constructors, Destructors, and Inheritance
  745.  
  746. Every class, including derived classes, must have a constructor. If a class
  747. declaration does not explicitly include a constructor, C++ creates a
  748. default constructor. The SafeStack class requires no initialization and its
  749. constructor simply calls its base class's constructor. C++ uses the
  750. following syntax for calling base class constructors:
  751.  
  752. SafeStack::SafeStack() : Stack()  // Constructor
  753. {
  754.     // Empty
  755. }
  756.  
  757. This statement arranges for the Stack constructor to be called before
  758. executing the body of the SafeStack constructor (which is empty, in this
  759. case).
  760.  
  761. Every class must also have a destructor, although C++ generates a default
  762. destructor if the programmer does not provide one. Destructors are called
  763. when an object is deleted, in the opposite order in which classes were
  764. constructed: destructors that belong to derived classes are executed before
  765. those supported by base classes.
  766.  
  767. Using Inheritance
  768.  
  769. There are two primary reasons for a programmer to use inheritance. The
  770. first is as an implementation convenience. It is often useful to create a
  771. new class by inheriting from an existing class that already implements some
  772. portion of the features needed in the new class. Often, the new class is a
  773. specialization of the original. For example, the SafeStack class
  774. discussed earlier added some error checking to the original Stack class. At
  775. other times, the new class may have little in common with the chosen
  776. superclass beyond the internal implementation. A programmer may choose a
  777. superclass because it supports a particularly efficient or complex
  778. algorithm. Or, perhaps the superclass simply contains a set of instance
  779. variables that are similar to those needed by the subclass.
  780.  
  781. We can refer to this second kind of inheritance as implementation
  782. inheritance, because its purpose is to share the implementation of the
  783. superclass. Implementation inheritance is useful primarily to the
  784. programmer creating the new subclass, because it saves implementation time
  785. or provides some other convenience to the programmer.
  786.  
  787.  The other approach is known as protocol inheritance. Here, the principal
  788. characteristic shared between the subclass and superclass(es) is the
  789. external interface, or protocol, defined by the superclass. Protocols are
  790. discussed further on page 20. Protocol inheritance may or may not benefit
  791. the programmer who creates the new subclass, but it almost always benefits
  792. the eventual user of the class.
  793.  
  794. A situation in which protocol inheritance could be used effectively would
  795. be a stack class that fixes one or more of the deficiencies of our simple
  796. Stack class. For example, suppose we need to write a ResizableStack class
  797. that is not limited to twenty members. Even if the ResizableStack class is
  798. derived from Stack, we will still need to write a fair amount of code. It
  799. might be necessary to change the internal representation of the stack, and
  800. the derived class might not even use the two data members supported by the
  801. Stack class. All member functions would need to be rewritten.
  802.  
  803. The class designer would gain no productivity by deriving from the Stack
  804. class in this situation.  However, the new class would inherit the external
  805. protocol of the Stack class, which could be of great importance to the
  806. eventual users of the class. This will become clearer as we discuss
  807. polymorphism and protocols in the following sections.
  808.  
  809. The programmer must decide which approach to inheritance is appropriate in
  810. any given situation. Happily, these two techniques work well together most
  811. of the time. A subclass that inherits the external protocol of another
  812. class is likely to share some of its implementation, and vice versa.
  813.  
  814. Even when a situation involves both protocol inheritance and implementation
  815. inheritance, it is important to be very careful when creating inheritance
  816. hierarchies. It is easy to misuse inheritance in ways that may be
  817. confusing. For example, assume there is an existing OceanLiner class that
  818. models a cruise ship. The OceanLiner class probably has characteristics
  819. (data members) such as:
  820.  
  821.      cargo    captain     passengerList
  822.     engine    fuelTank    schedule
  823.  
  824. The OceanLiner class would also support operations such as:
  825.  
  826.     move    refuel    loadPassengers    
  827.  
  828. Now, suppose we need an Airplane class. We could derive this class from
  829. OceanLiner. An Airplane class would need to support most or all the data
  830. members and member functions defined by the OceanLiner class. Inheriting
  831. from OceanLiner would save implementation time (it would require less
  832. typing), and would also share a large part of the OceanLiner's external
  833. protocol. But does it make sense to view an Airplane as a specialization of
  834. an OceanLiner? Probably not. This use of inheritance is likely to cause
  835. problems later, and will certainly be confusing to anyone who uses the
  836. Airplane class.
  837.  
  838. Situations like this usually indicate the need for some restructuring of
  839. the inheritance hierarchy.  The Airplane and OceanLiner classes do have
  840. something in common, obviously. Each shares the characteristics of a
  841. Vehicle, or perhaps a PassengerVehicle. Both should probably be derived
  842. from a common ancestor that defines those attributes common to both. But in
  843. most cases, they should not inherit from each other.
  844.  
  845. Polymorphism
  846.  
  847. An important characteristic of many object-oriented languages is
  848. polymorphism. Polymorphism allows multiple objects to respond to the same
  849. message, while allowing each object to interpret the message in its own
  850. way.
  851.  
  852. A precise (and commonly agreed upon) definition of polymorphism seems to be
  853. almost as elusive as a definition of object-oriented programming. The word
  854. literally refers to the ability to take on "many forms." Stroustrup
  855. [Stroustrup90] calls polymorphism "the ability to call a variety of
  856. functions using exactly the same interface as provided by (C++) virtual
  857. functions." Booch [Booch90] leans toward a slightly more theoretical
  858. definition and says that "polymorphism is a concept in type theory in which
  859. a name may denote objects of many different classes that are related by
  860. some common superclass". While Booch's definition may appear at first to
  861. have little relationship to Stroustrup's statement, the theoretical concept
  862. is the basis of the mechanism that enables polymorphic behavior in C++.
  863.  
  864. In C++, polymorphism is supported by virtual member functions. A function
  865. can be declared to be virtual by preceding the function declaration with
  866. the keyword virtual. For example the following class defines a virtual
  867. function, xyz():
  868.  
  869. class X {
  870.   public:
  871.     virtual int xyz();
  872. }
  873.  
  874. Normally, the compiler can determine exactly what member function should be
  875. called in any given situation at compile time. However, when a member
  876. function is declared to be virtual, such decisions are delayed until
  877. runtime. When virtual member functions are used, the actual function to be
  878. invoked in response to a given message depends on the dynamic type of the
  879. object, not the declared type. To use virtual functions to achieve
  880. polymorphic behavior, the objects involved must belong to classes derived
  881. from a common base class, and the base class must declare the desired
  882. member function to be virtual.
  883.  
  884. It's easiest to show how virtual functions work with an example. Let's
  885. implement two classes, class A and class B. Class A supports one member
  886. function, print(). For purposes of illustration, this print() member
  887. function just prints a message reporting the member function and name of
  888. the class. Notice that the print() member function is not declared to be
  889. virtual.
  890.  
  891. class A {
  892.  
  893.   public:
  894.  
  895.     // A NON-virtual member function
  896.     void print() { cout << "A::print \n"; }
  897. };  
  898.  
  899. Now let's derive class B from class A. Class B also supports a similar, non-virtual, print() 
  900. member function:
  901.  
  902. class B: public A {
  903.  
  904.   public:
  905.  
  906.     // A NON-virtual member function
  907.     void print() { cout << "B::print \n"; } 
  908. };
  909.  
  910. Now, look at the following code that uses classes A and B.The body of the
  911. program instantiates two objects, one belonging to class A and the other to
  912. class B. The program sends the print message to each object and then passes
  913. each object to a function named printObj(). This function expects a pointer
  914. to an object belonging to class A as an argument. C++ allows pointers to
  915. objects that belong to classes derived from the declared type to be passed
  916. to this function as well.
  917.  
  918. main()
  919. {
  920.     A *a = new A();    // Create an A object
  921.     B *b = new B();    // Create a B object
  922.     
  923.     a->print();        // Print the A object
  924.     b->print();        // Print the B object
  925.     printObj ( a );    // Print the A object
  926.     printObj ( b );    // Print the B object
  927. }
  928.  
  929. void printObj ( A *obj )
  930. {
  931.      obj->print();     // Print the given object
  932. }
  933.  
  934. When this program is executed, it prints the following results:
  935.  
  936. A::print
  937. B::print
  938. A::print
  939. A::print
  940.  
  941. The first three lines seem fine, but what about the fourth line? Because
  942. the printObj() function expects an object belonging to class A, the
  943. A::print() member function is executed, even though a class B object is
  944. passed to the function. This is probably not what most programmers would
  945. intend.
  946.  
  947. Virtual functions fix this problem. With virtual functions, the member
  948. function to be executed depends on the actual (dynamic) type of the object,
  949. not the statically declared type. Let's rewrite classes A and B to use a
  950. virtual print() member function:
  951.  
  952. class A {
  953.  
  954.   public:
  955.  
  956.     virtual void print() { cout << "A::print \n"; }
  957. };  
  958.  
  959. class B: public A {
  960.  
  961.   public:
  962.  
  963.     void print() { cout << "B::print \n"; } // Virtual because of 
  964.                                             // declaration in A
  965. };
  966.  
  967. Now, if we use these classes in the previous example, the results are more
  968. like what one would expect:
  969.  
  970. A::print
  971. B::print
  972. A::print
  973. B::print
  974.  
  975.  To achieve polymorphic behavior, all classes involved must be derived from
  976. a common class.  In addition, the common base class must declare all the
  977. virtual functions of interest. For example, suppose we create a new class C
  978. that adds an additional member function:
  979.  
  980. class C : public A {
  981.  
  982.   public:
  983.  
  984.     void print() { cout << "C::print \n"; }
  985.     char *name() { return ( "C" ); }
  986. };
  987.  
  988. Now let's look at an example that includes class C. The following code
  989. segment declares three pointers to objects of type A, but instantiates and
  990. assigns objects of types A, B, and C:
  991.  
  992. main()
  993. {
  994.     A *a = new A();  // Create an A object
  995.     A *b = new B();  // Create a B object
  996.     A *c = new C();  // Create a C object
  997.  
  998.     a->print();      // print A
  999.     b->print();      // print B
  1000.     c->print();      // print C
  1001.     cout << c->name() << "\n"; // Error - A does not support name()
  1002. }
  1003.  
  1004. All three objects can accept and respond appropriately to the print()
  1005. message, but trying to use the name() member function causes an error at
  1006. compile time. Because object "c" is declared to belong to class A, the
  1007. C::name() function cannot be accessed. Class A does not support the name()
  1008. member function, and therefore name() does not participate in the
  1009. polymorphic behavior of these classes.
  1010.  
  1011. In most cases, the choice of which member functions should be declared as
  1012. virtual is a design decision, and depends on how the class is being used.
  1013. However, destructors should almost always be declared virtual. This insures
  1014. that all class destructors are called correctly when an object is
  1015. destroyed, regardless of how the object is declared. See [Stroustrup91] for
  1016. more information on virtual destructors.
  1017.  
  1018. Polymorphism can make programs simpler to write and easier to design. The
  1019. burden of knowing how each type of object implements a particular operation
  1020. is placed on the class designer instead of the programmer using the class.
  1021. The programmer who uses a class needs to understand only its external
  1022. interface and the ultimate result of sending any particular message to an
  1023. object belonging to that class. Polymorphism also makes systems easier to
  1024. extend. With polymorphism, existing parts of a system can send the same
  1025. messages to new objects that support the same methods as other objects
  1026. already in the system.
  1027.  
  1028. Many people maintain that polymorphism is the key to object-oriented
  1029. programming. Some languages that appear on the surface to be
  1030. object-oriented support only data abstraction. Data abstraction and
  1031. encapsulation are very important, but polymorphism adds a new flavor that
  1032. many feel is an essential part of object-oriented systems.
  1033.  
  1034. Programmers that do not use polymorphism often find themselves using switch
  1035. statements to perform different actions depending on the type of a
  1036. particular piece of data. In object-oriented programming, switch statements
  1037. are viewed with the same disdain previously reserved for the "go to"
  1038. statement. A switch statement in a C++ program should be viewed as a
  1039. warning that the program may not be structured correctly, and may not be
  1040. taking advantage of polymorphism.
  1041.  
  1042. Protocols
  1043.  
  1044. The concept of a protocol is closely related to polymorphism. A protocol is
  1045. simply a well-defined interface, usually made up of the set of methods
  1046. supported by the class. For example, we could decide that the methods
  1047. push() and pop() define the complete protocol required for any stack class.
  1048. That is, for any class to claim that it obeys the stack protocol, it must
  1049. support at least the push() and pop() methods. Other stack classes might
  1050. support additional methods as well, but as long as they support these two
  1051. methods, we can say they support the stack protocol.
  1052.  
  1053. In the example in the previous section, class A defines a protocol in the
  1054. form of a virtual print() member function that is also supported by the B
  1055. and C derived classes. However, the name() member function is not part of
  1056. the protocol defined by A. Of course, this function could be part of an
  1057. extended protocol defined for C and its derived classes.
  1058.  
  1059. The notion of a protocol is crucial to the design of reusable components
  1060. and object-oriented architectures in general. Classes with well-defined and
  1061. enforceable protocols tend to be easier to reuse and maintain. One value of
  1062. object-oriented programming is that external interfaces tend to be more
  1063. explicit. Minimizing external references and formalizing the interface
  1064. between an object and the outside world as much as possible usually results
  1065. in code that is easier to use and maintain. Such code is usually easier to
  1066. reuse as well. However, it may be more difficult to write. It is often
  1067. tempting to throw together something that works and "fix it later," rather
  1068. than identifying and implementing a complete and correct protocol.
  1069.  
  1070. Even when using non-object-oriented techniques, every function or module
  1071. has an external protocol. However, this external protocol may not be well
  1072. defined or understood. For example, if a function relies on the value of a
  1073. global variable, or sets a global variable, then that global variable
  1074. becomes part of the function's protocol. However, this type of protocol may
  1075. not be obvious to those who use the function.
  1076.  
  1077. Software often exhibits more subtle protocols. For example, the order in
  1078. which a particular set of functions is called may be important. Such
  1079. implicit protocols are hard to document, use, and maintain. They provide a
  1080. likely source of errors because these "hidden" protocols may not be
  1081. immediately obvious to a programmer who is unfamiliar with a given piece of
  1082. code. Implicit protocols can be found in object-oriented systems and
  1083. non-object-oriented systems alike. If a programmer must send a sequence of
  1084. messages to an object in a particular order, the sequence becomes part of
  1085. the object's protocol.
  1086.  
  1087. When using inheritance, classes have two distinct protocols. The first is
  1088. the external protocol that determines how the outside world interacts with
  1089. the class. The second is the protocol between a class and its subclasses.
  1090. Few object-oriented languages offer any formal way to specify the protocol
  1091. between a class and its subclasses. However, C++ provides a mechanism that
  1092. allows the class designer to declare data members and member functions as
  1093. either private or protected. Protected members cannot be seen by the
  1094. outside world, but are freely available to derived classes, while private
  1095. members cannot be seen, even by derived classes. Careful use of the public,
  1096. private, and protected declarations allow classes to specify both the
  1097. external and subclass protocols.
  1098.  
  1099. Protocols and C++
  1100.  
  1101. C++ supports the idea of an abstract class, whose primary purpose is to
  1102. define and enforce a protocol. An abstract class cannot be instantiated. It
  1103. serves only to specify the external protocol for classes derived from it.
  1104. In C++, an abstract class is any class that declares a pure virtual member
  1105. function. C++ uses the following syntax to declare a pure virtual function:
  1106.  
  1107. virtual function() = 0;
  1108.  
  1109. Any class that declares such a member function cannot be instantiated. All
  1110. instantiable classes that derive from a C++ abstract class must implement
  1111. all pure virtual functions. Attempting to instantiate an abstract class
  1112. directly generates an error at compile time. Pure virtual functions provide
  1113. a way to enforce a protocol, without implementing all member functions in a
  1114. base class. The base class simply declares that all instantiable derived
  1115. classes must implement the function.
  1116.  
  1117. For example, the following abstract class defines a stack protocol:
  1118.  
  1119. class StackProtocol {
  1120.  
  1121.    public:
  1122.  
  1123.     StackProtocol() { }
  1124.     virtual ~StackProtocol() { }
  1125.     virtual void push ( int ) = 0;
  1126.     virtual int pop() = 0;
  1127. };
  1128.  
  1129. This class does not implement any behavior of its own and contains no data
  1130. members. It simply declares a set of pure virtual functions that designates
  1131. a protocol for a stack. Any instantiable class that derives from the
  1132. StackProtocol class must implement the push() and pop() member functions,
  1133. as declared by the StackProtocol class. This forces all derived classes to
  1134. obey the protocol defined by its base class.
  1135.  
  1136. 3    Non-object-oriented Features of C++
  1137.  
  1138. Although C++ is similar to C in many ways, C++ has many additional
  1139. features, including some that have little or no direct relationship to
  1140. object-oriented programming. A few of these features warrant special
  1141. mention because they enhance the language's ability to support
  1142. object-oriented programming. The features introduced in the following
  1143. sections are: inline functions, function overloading, and the use of the
  1144. const declaration.
  1145.  
  1146. Inline Functions
  1147.  
  1148. Programmers are often concerned with the efficiency of object-oriented
  1149. systems. For example, object-oriented programs tend to contain many small
  1150. functions and methods, and the overhead of calling these functions can
  1151. sometimes become a problem. C++ allows programmers to declare functions as
  1152. inline. When possible, C++ replaces calls to inline functions with actual
  1153. code in each function, eliminating the cost of a function call. An inline
  1154. declaration is an optimization request, much like a register declaration.
  1155. The compiler may or may not be able to fulfill the request.
  1156.  
  1157. Inline functions are declared with the keyword inline, as follows:
  1158.  
  1159. inline int square ( int x )
  1160. {
  1161.     return ( x * x );
  1162. }
  1163.  
  1164. The C++ translator or compiler attempts to substitute the body of this
  1165. function for any call to the function, after making the appropriate
  1166. parameter substitutions. Note that inline functions are not macros,
  1167. although an inline function can be used effectively in many situations
  1168. where C programmers would use a macro.
  1169.  
  1170. Member functions can also be declared inline, either explicitly or
  1171. implicitly. Explicit inline member functions are also declared using the
  1172. inline keyword, as in:
  1173.  
  1174. class MyClass {
  1175.  
  1176.   public:
  1177.  
  1178.     // Various member functions ...
  1179.  
  1180.     inline square ( int );
  1181. };
  1182.  
  1183. int MyClass::square ( int x )
  1184. {
  1185.    return ( x * x );
  1186. }
  1187.  
  1188. The body of an inline member function can also be provided inside the class
  1189. declaration, like this:
  1190.  
  1191. class MyClass {
  1192.  
  1193.   public:
  1194.  
  1195.     MyClass();
  1196.     int square ( int x ) { return ( x * x ); }
  1197. }
  1198.  
  1199. Here, the square() member function is implicitly declared as an inline
  1200. function and no inline keyword is required.
  1201.  
  1202. Inline functions are particularly useful for the small access member
  1203. functions often used in classes. Inline functions allow the programmer to
  1204. maintain a class's encapsulation, without compromising efficiency.
  1205.  
  1206. Notice that the idea of replacing a function call, at compile time, with
  1207. the code that implements the body of the function is fundamentally at odds
  1208. with the concept of virtual functions, where the actual function to be
  1209. called is not known until runtime. Most C++ compilers or translators
  1210. attempt to deal with inline virtual functions in those cases where the
  1211. correct function can be determined statically. However, in most cases,
  1212. inline virtual functions should be avoided.
  1213.  
  1214. Function Overloading
  1215.  
  1216. C++ allows the programmer to declare multiple functions with the same name,
  1217. as long as the functions require different argument types or different
  1218. numbers of arguments. Such functions are said to be overloaded. Overloaded
  1219. functions can be very useful in writing C++ classes.
  1220.  
  1221. For example, assume that the mail system example discussed earlier also
  1222. supports several possible types of output devices (printers), and that
  1223. these devices are modeled as objects. We could have a LinePrinter class, a
  1224. LaserPrinter class, and a Typesetter class. We could now define the print
  1225. protocol for all objects in the mail system in a PrintableObject class that
  1226. supports these different printers.
  1227.  
  1228. The PrintableObject class might need to prepare or format the data
  1229. differently for different types of printers. We could handle this situation
  1230. by designing the class to support member functions like printToLaser(),
  1231. printToLinePrinter(), and so on, but the result would be very awkward.
  1232. Another approach would be to pass an argument to the print function
  1233. specifying the desired type of printer. A single function could check the
  1234. printer object's type and perform the action appropriate for the type.
  1235.  
  1236. Overloaded functions allow programmers to achieve the same effect without
  1237. checking the type at runtime. For example, C++ allows the multiple printer
  1238. scenario to be handled as follows:
  1239.  
  1240. class PrintableObject {
  1241.  
  1242.   public:
  1243.  
  1244.     // Other member functions
  1245.   
  1246.     print ( LinePrinter * );
  1247.     print ( LaserPrinter * );
  1248.     print ( Typesetter * );
  1249.     print ( LinePrinter *, LaserPrinter * ); // Print to both
  1250. }
  1251.  
  1252. Now, the PrintableObject class supports four different member functions,
  1253. all named print().  Which member function is called in response to any
  1254. given print message depends on the number and type of the arguments. Notice
  1255. that unlike virtual functions, the compiler can determine which overloaded
  1256. function is called at compile-time. Member functions can, of course, be
  1257. both virtual and overloaded at the same time.
  1258.  
  1259. It is often convenient to overload a class constructor to provide more than
  1260. one way to initialize a class. For example, we might wish to implement a
  1261. ResizableStack class that supports either a variable or a fixed sized
  1262. stack. This class could have two constructors, declared as:
  1263.  
  1264. class ResizableStack {
  1265.  
  1266.   public:
  1267.  
  1268.     // Various members
  1269.  
  1270.     ResizableStack();               // Use a dynamically sized stack
  1271.     ResizableStack ( int size );    // Pre-allocate initial stack size
  1272.     // ...
  1273.  
  1274. The constructor with no arguments would allocate a small stack, using some
  1275. default size. Applications that anticipate storing a specific amount of
  1276. data can call the other constructor to provide this information to the
  1277. class.
  1278.  
  1279. C++ also allows function prototypes to declare default values for some
  1280. parameters. For example, the above example could also be implemented with a
  1281. single constructor, with an optional argument. This is done by assigning a
  1282. default value to the parameter in the prototype, as shown in the following
  1283. example:
  1284.  
  1285. class ResizableStack {
  1286.  
  1287.   public:
  1288.  
  1289.     // Various members
  1290.  
  1291.     ResizableStack ( int size = 20 ); // Stack size is 20 by default
  1292.     // ...
  1293.  
  1294. The const Declaration
  1295.  
  1296. C++ allows the programmer to declare variables, as well as values passed or
  1297. returned from functions, to be immutable, or const. A variable declared to
  1298. be const cannot be altered. The const declaration is particularly useful
  1299. when writing object-oriented programs in C++ because it allows the
  1300. programmer to improve the encapsulation of a class when passing or
  1301. returning pointers.
  1302.  
  1303. The const declaration is particularly useful for improving encapsulation in
  1304. C++ classes. For example, the following String class supports an access
  1305. function that returns a character string.  Because a string is just an
  1306. array of characters, simply returning the address of the string would allow
  1307. the string to be manipulated, and even overwritten, from outside the class.
  1308. The following implementation uses the const declaration to prevent the
  1309. string from being modified outside the class.
  1310.  
  1311. class String {
  1312.  
  1313.   private:
  1314.  
  1315.     char *_data;      // The characters in the string
  1316.     int   _length;    // Number of characters in string
  1317.  
  1318.   public:
  1319.  
  1320.     String ( char *str ) // Constructor - declared inline
  1321.     { 
  1322.         _data = str; _length = strlen ( str ); 
  1323.     }
  1324.     const char *const data() { return ( _data ); } // Accessor for _data
  1325. };
  1326.  
  1327. Here, the data() function is declared to return a const pointer to a const
  1328. value. Neither the pointer to the string, nor the string itself can be
  1329. modified. This use of const does impose some restrictions on how the class
  1330. can be used. For example, we cannot assign the result of the data() member
  1331. function to another non-const variable:
  1332.  
  1333. String *nameString = new String ( "George" );
  1334. char   *name;
  1335. name = nameString->data();                // Error!
  1336. if ( strcmp ( name, "George" ) != 0 )
  1337.     // ...
  1338.  
  1339. However, because data() is an inline function, it can usually be used
  1340. directly wherever it is needed, with no loss of efficiency, and without
  1341. compromising the encapsulation of the String class:
  1342.  
  1343. String *nameString = new String ( "George" );
  1344.  
  1345. if ( strcmp ( nameString->data(), "George" ) != 0 )
  1346.     // ...
  1347.  
  1348. 4    Summary
  1349.  
  1350. This tutorial introduced some basic object-oriented concepts and discussed
  1351. the mechanisms that support object-oriented programming in C++. The syntax
  1352. of C++ is very similar to that of C, and it is possible to program in C++
  1353. using the same style used to program in C. However, to get the most out of
  1354. C++, programmers should learn to use the new facilities provided by the
  1355. language, and develop an object-oriented programming style. In addition to
  1356. classes, member functions, and inheritance, C++ also supports many useful
  1357. non-object-oriented features such as inline functions and function
  1358. overloading.
  1359.